package demo;

import java.util.Vector;
import gateway.ontology.GatewayOntology;
import gateway.ontology.classes.GetRemoteAgents;
import gateway.ontology.classes.RegisterAgent;
import gateway.ontology.classes.UnregisterAgent;
import gateway.ontology.classes.basic_translation.Header;
import gateway.ontology.classes.basic_translation.Message;
import gateway.ontology.classes.basic_translation.ReceivedMessage;
import gateway.ontology.classes.basic_translation.SendMessage;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Done;
import jade.content.onto.basic.Result;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.AchieveREInitiator;
import jade.util.leap.List;

public class DemoAgent extends Agent {

	private AID gateway = null;
	private Vector mapsAgents = null;

	Codec codec = new SLCodec();
	Ontology ontology = GatewayOntology.getInstance();

	class MainBehaviour extends TickerBehaviour {

		public MainBehaviour(Agent a, long period) {
			super(a, period);
		}

		@Override
		protected void onTick() {
			for (int i = 0; i < mapsAgents.size(); i++)
				myAgent.addBehaviour(new SendMessageBehaviour(
						(String) mapsAgents.get(i), myAgent));
		}
	}

	class HandleInformBehaviour extends CyclicBehaviour {

		MessageTemplate mt;

		public HandleInformBehaviour(Agent agent) {
			super(agent);

			this.mt = MessageTemplate
					.and(MessageTemplate.MatchPerformative(ACLMessage.INFORM),
							MessageTemplate.and(
									MessageTemplate.MatchOntology(ontology
											.getName()),
									MessageTemplate.and(
											MessageTemplate.MatchLanguage(codec
													.getName()),
											MessageTemplate.not(MessageTemplate
													.MatchProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST)))));
		}

		@Override
		public void action() {
			ACLMessage message = myAgent.receive(mt);
			if (message != null) {
				ContentElement ce = null;
				try {
					ce = myAgent.getContentManager().extractContent(message);
					if (ce instanceof ReceivedMessage) {
						Header header = ((ReceivedMessage) ce)
								.getMessageWithHeader();
						if (header instanceof Message) {
							Message msg = (Message) header;
							System.out.println("Agent " + myAgent.getName()
									+ " gets message from " + msg.getMapsID()
									+ " with answer : "
									+ msg.getParameter("ANSWER"));
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			} else
				block();
		}

	}

	class SendMessageBehaviour extends OneShotBehaviour {
		private String target;

		public SendMessageBehaviour(String target, Agent a) {
			super(a);
			this.target = target;
		}

		@Override
		public void action() {
			try {
				// create ACLMessage
				ACLMessage aclmessage = new ACLMessage(ACLMessage.REQUEST);
				aclmessage.addReceiver(gateway);
				aclmessage.setLanguage(codec.getName());
				aclmessage.setOntology(ontology.getName());
				aclmessage
						.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);

				// create an agent action
				SendMessage sendMessage = new SendMessage();
				sendMessage.setAgentAID(myAgent.getAID());
				sendMessage.setMapsID(target);
				// by default, each new message is set to Event.MSG and
				// Event.NOW
				sendMessage.pushParameter("REQUEST", "MAPSID");

				// create an action (required by SL codec)
				Action action = new Action();
				action.setActor(getAID());
				action.setAction(sendMessage);

				myAgent.getContentManager().fillContent(aclmessage, action);

				myAgent.addBehaviour(new AchieveREInitiator(myAgent, aclmessage) {

					@Override
					protected void handleInform(ACLMessage inform) {
						ContentElement ce = null;
						try {
							ce = myAgent.getContentManager().extractContent(
									inform);
							if (ce instanceof Done) {
								Done done = (Done) ce;
								System.out.println("Agent " + myAgent.getName()
										+ " queued message : "
										+ inform.toString());
							} else {
								System.out.println("Agent "
										+ myAgent.getName()
										+ " does not understand the response : "
										+ inform.toString());
								myAgent.doDelete();
							}
						} catch (Exception e) {
							e.printStackTrace();
							myAgent.doDelete();
						}
					}

					@Override
					protected void handleFailure(ACLMessage failure) {
						System.out.println("Agent " + myAgent.getName()
								+ " gets failure message :"
								+ failure.getContent());
						myAgent.doDelete();
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
				myAgent.doDelete();
			}
		}
	}

	class UpdateMAPSAgents extends TickerBehaviour {

		public UpdateMAPSAgents(Agent a, long period) {
			super(a, period);
		}

		@Override
		protected void onTick() {
			try {
				// create ACLMessage
				ACLMessage aclmessage = new ACLMessage(ACLMessage.REQUEST);
				aclmessage.addReceiver(gateway);
				aclmessage.setLanguage(codec.getName());
				aclmessage.setOntology(ontology.getName());
				aclmessage
						.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);

				// create an agent action
				GetRemoteAgents getRemoteAgents = new GetRemoteAgents();

				// create an action (required by SL codec)
				Action action = new Action();
				action.setActor(getAID());
				action.setAction(getRemoteAgents);

				myAgent.getContentManager().fillContent(aclmessage, action);

				myAgent.addBehaviour(new AchieveREInitiator(myAgent, aclmessage) {

					@Override
					protected void handleInform(ACLMessage inform) {
						ContentElement ce = null;
						try {
							ce = myAgent.getContentManager().extractContent(
									inform);
							if (ce instanceof Result) {
								Result result = (Result) ce;
								List agents = result.getItems();
								// clean
								mapsAgents.removeAllElements();
								// copy
								for (int i = 0; i < agents.size(); i++)
									mapsAgents.add(agents.get(i));
								System.out.println("Agent " + myAgent.getName()
										+ " updates its list of MAPS agents.");
							} else {
								System.out.println("Agent "
										+ myAgent.getName()
										+ " does not understand the response : "
										+ inform.toString());
								myAgent.doDelete();
							}
						} catch (Exception e) {
							e.printStackTrace();
							myAgent.doDelete();
						}
					}

					@Override
					protected void handleFailure(ACLMessage failure) {
						System.out.println("Agent " + myAgent.getName()
								+ " gets failure message :"
								+ failure.getContent());
						myAgent.doDelete();
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	class RegisterBehaviour extends OneShotBehaviour {

		public RegisterBehaviour(Agent agent) {
			super(agent);
		}

		@Override
		public void action() {
			try {
				// create ACLMessage
				ACLMessage aclmessage = new ACLMessage(ACLMessage.REQUEST);
				aclmessage.addReceiver(gateway);
				aclmessage.setLanguage(codec.getName());
				aclmessage.setOntology(ontology.getName());
				aclmessage
						.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);

				// create agent action
				RegisterAgent register = new RegisterAgent();
				register.setAgentAID(getAID());
				Object[] args = myAgent.getArguments();
				if (args.length == 1 && args[0] instanceof String) {
					register.setSuffix((String) args[0]);
				}

				// create action
				Action action = new Action();
				action.setActor(getAID());
				action.setAction(register);

				myAgent.getContentManager().fillContent(aclmessage, action);

				myAgent.addBehaviour(new AchieveREInitiator(myAgent, aclmessage) {

					@Override
					protected void handleInform(ACLMessage inform) {
						ContentElement ce = null;
						try {
							ce = myAgent.getContentManager().extractContent(
									inform);
							if (ce instanceof Done) {
								Done done = (Done) ce;
								System.out.println("Agent " + myAgent.getName()
										+ " registered.");
								// update list of MAPS agents every 1 minute
								myAgent.addBehaviour(new UpdateMAPSAgents(
										myAgent, 60000));
								// send request to send an message to all MAPS
								// agents every 30s
								myAgent.addBehaviour(new MainBehaviour(myAgent,
										30000));
								// handle INFORM messages (replays from MAPS)
								myAgent.addBehaviour(new HandleInformBehaviour(
										myAgent));
							} else {
								System.out.println("Agent "
										+ myAgent.getName()
										+ " does not understand the response : "
										+ inform.toString());
								myAgent.doDelete();
							}
						} catch (Exception e) {
							e.printStackTrace();
							myAgent.doDelete();
						}
					}

					@Override
					protected void handleFailure(ACLMessage failure) {
						System.out.println("Agent " + myAgent.getName()
								+ " gets failure message :"
								+ failure.getContent());
						myAgent.doDelete();
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
				myAgent.doDelete();
			}
		}
	}

	/*
	 * Ask DF agent for registered Gateway agents.
	 */
	private AID askForGatewayAgent() {
		DFAgentDescription template = new DFAgentDescription();

		// fill a service description
		ServiceDescription sd = new ServiceDescription();
		sd.setType("MAPS-JADE-gateway");
		sd.addProtocols(FIPANames.InteractionProtocol.FIPA_REQUEST);
		sd.addLanguages(codec.getName());
		sd.addOntologies(ontology.getName());

		template.addServices(sd);
		try {
			DFAgentDescription[] result = DFService.search(this, template);
			if (result.length > 0)
				return result[0].getName();
		} catch (FIPAException fe) {
			fe.printStackTrace();
		}
		return null;
	}

	@Override
	public void setup() {
		mapsAgents = new Vector();

		System.out.println("DemoAgent " + getAID().getName() + " is  ready.");

		// register SL codec
		getContentManager().registerLanguage(codec);
		// register ontologies
		getContentManager().registerOntology(ontology);

		// get gateway information
		gateway = askForGatewayAgent();

		if (gateway != null) {
			addBehaviour(new RegisterBehaviour(this));
		} else
			// if no gateway agent is available, terminate
			doDelete();
	}

	@Override
	public void takeDown() {
		// unregister the DemoAgent
		try {
			ACLMessage aclmessage = new ACLMessage(ACLMessage.REQUEST);
			aclmessage.addReceiver(gateway);
			aclmessage.setLanguage(codec.getName());
			aclmessage.setOntology(ontology.getName());
			aclmessage.setProtocol(FIPANames.InteractionProtocol.FIPA_REQUEST);

			// create agent action
			UnregisterAgent unregister = new UnregisterAgent();
			unregister.setAgentAID(getAID());

			// create action
			Action action = new Action();
			action.setActor(getAID());
			action.setAction(unregister);

			getContentManager().fillContent(aclmessage, action);
			
			// send request and terminate
			send(aclmessage);
		} catch (CodecException e) {
			e.printStackTrace();
		} catch (OntologyException e) {
			e.printStackTrace();
		}
		System.out.println("DemoAgent " + getAID().getName() + " terminated.");
	}
}
